﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections;
using System.Windows.Input;
using System.Windows.Data;

namespace Kokomo.Controls
{
	public class TabbedDocumentView : TabbedDocumentBase
	{
		static TabbedDocumentView()
		{
			DefaultStyleKeyProperty.OverrideMetadata(typeof(TabbedDocumentView), new FrameworkPropertyMetadata(typeof(TabbedDocumentView)));

			FloatCommand = new RoutedCommand();
		}

		public TabbedDocumentView()
		{
			this._groups = new ObservableCollection<DocumentTabGroup>();
			this._groupTable = new Dictionary<int, DocumentTabGroup>();

			this.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);
			this.ItemContainerGenerator.ItemsChanged += new ItemsChangedEventHandler(ItemContainerGenerator_ItemsChanged);

			this.CommandBindings.Add(new CommandBinding(FloatCommand, this.FloatCommand_Executed));
		}

		#region Item stuff
		void ItemContainerGenerator_ItemsChanged(object sender, ItemsChangedEventArgs e)
		{
			if (e.Action == NotifyCollectionChangedAction.Add)
			{
				IItemContainerGenerator generator = (IItemContainerGenerator)sender;
				using (IDisposable generation = generator.StartAt(e.Position, GeneratorDirection.Forward))
				{
					for (int i = 0; i < e.ItemCount; i++)
					{
						DocumentItem itemContainer = (DocumentItem)generator.GenerateNext();
						int itemIndex = this.ItemContainerGenerator.IndexFromContainer(itemContainer);
						generator.PrepareItemContainer(itemContainer);
						object item = this.ItemsSource.OfType<object>().ElementAt(itemIndex);// this.ItemContainerGenerator.ItemFromContainer(itemContainer);
						//DocumentItem itemContainer = (DocumentItem)this.ItemContainerGenerator.ContainerFromItem(item);
						itemContainer.DataContext = item;

						int group = itemContainer.Group;
						DocumentTabGroup groupContainer = null;
						if (!this.IsGroupValid(group))
						{
							groupContainer = this.SelectedGroup;
							if (groupContainer == null)
							{
								this.EnsureDefaultGroup();
								groupContainer = this.Groups.First();
							}
							group = groupContainer._group;

							itemContainer.Group = group;
						}

						//if (itemContainer.IsFloating)
						//{
						//    Window window = new Window();
						//    if (this.FloatingWindowTemplate != null)
						//        window.Template = this.FloatingWindowTemplate;
						//    if (this.FloatingWindowStyle != null)
						//        window.Style = this.FloatingWindowStyle;

						//    window.DataContext = item;
						//    window.Content = itemContainer;
						//    window.Owner = Application.Current.MainWindow;
						//}
						//else
						{
							if (groupContainer == null)
								groupContainer = this.GetGroupContainer(group);
							((IList)groupContainer.ItemsSource).Add(itemContainer);
						}

						itemContainer.GroupChanged += new EventHandler(Item_GroupChanged);
						itemContainer.IsFloatingChanged += new EventHandler(Item_IsFloatingChanged);
					}
				}
			}
			else if (e.Action == NotifyCollectionChangedAction.Remove)
			{
				IItemContainerGenerator generator = (IItemContainerGenerator)sender;
				int startIndex = e.Position.Index;// generator.IndexFromGeneratorPosition(e.Position.Index);
			}
		}
		void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
		{

		}

		protected override void ClearContainerForItemOverride(DependencyObject element, object item)
		{
			DocumentItem container = (DocumentItem)element;
			IList<DocumentItem> groupList = this.GetGroupItemsSource(container.Group);
			if (groupList != null)
				groupList.Remove(container);

			base.ClearContainerForItemOverride(element, item);
		}

		protected override DependencyObject GetContainerForItemOverride()
		{
			return new DocumentItem();
		}

		protected override bool IsItemItsOwnContainerOverride(object item)
		{
			return (item is DocumentItem);
		}
		#endregion

		public Orientation SplitOrientation
		{
			get { return (Orientation)GetValue(SplitOrientationProperty); }
			set { SetValue(SplitOrientationProperty, value); }
		}

		// Using a DependencyProperty as the backing store for SplitOrientation.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty SplitOrientationProperty =
			DependencyProperty.Register("SplitOrientation", typeof(Orientation), typeof(TabbedDocumentView), new UIPropertyMetadata(Orientation.Vertical));

		#region Styles and templates
		public Style TabStyle
		{
			get { return (Style)GetValue(TabStyleProperty); }
			set { SetValue(TabStyleProperty, value); }
		}

		// Using a DependencyProperty as the backing store for TabStyle.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty TabStyleProperty =
			DependencyProperty.Register("TabStyle", typeof(Style), typeof(TabbedDocumentView), new UIPropertyMetadata(null));

		public DataTemplate TabTemplate
		{
			get { return (DataTemplate)GetValue(TabTemplateProperty); }
			set { SetValue(TabTemplateProperty, value); }
		}

		// Using a DependencyProperty as the backing store for TabTemplate.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty TabTemplateProperty =
			DependencyProperty.Register("TabTemplate", typeof(DataTemplate), typeof(TabbedDocumentView), new UIPropertyMetadata(null));

		public Style TabGroupStyle
		{
			get { return (Style)GetValue(TabGroupStyleProperty); }
			set { SetValue(TabGroupStyleProperty, value); }
		}

		// Using a DependencyProperty as the backing store for TabGroupStyle.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty TabGroupStyleProperty =
			DependencyProperty.Register("TabGroupStyle", typeof(Style), typeof(TabbedDocumentView), new UIPropertyMetadata(null));
		#endregion

		#region Groups
		private int _nextGroupID;

		private ObservableCollection<DocumentTabGroup> _groups;
		public IEnumerable<DocumentTabGroup> Groups { get { return this._groups; } }

		private Dictionary<int, DocumentTabGroup> _groupTable;

		protected void EnsureDefaultGroup()
		{
			if (this._groups.Count == 0)
				this.CreateNewGroup();
		}

		protected void CreateNewGroup()
		{
			lock (this)
			{
				int id = this._nextGroupID++;
				DocumentTabGroup group = new DocumentTabGroup()
				{
					_group = id,
					Style = this.TabGroupStyle,
					ItemContainerStyle = this.TabStyle,
					ItemTemplate = this.TabTemplate,
					ContentTemplate = this.ContentTemplate,
					ContentTemplateSelector = this.ContentTemplateSelector,
					ContentStringFormat = this.ContentStringFormat,
					ItemsSource = new ObservableCollection<DocumentItem>()
				};
				group.IsKeyboardFocusWithinChanged += new DependencyPropertyChangedEventHandler(group_IsKeyboardFocusWithinChanged);
				this._groups.Add(group);
				this._groupTable.Add(id, group);
			}
		}

		public DocumentTabGroup SelectedGroup
		{
			get { return (DocumentTabGroup)GetValue(SelectedGroupProperty); }
			set { SetValue(SelectedGroupProperty, value); }
		}

		// Using a DependencyProperty as the backing store for SelectedGroup.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty SelectedGroupProperty =
			DependencyProperty.Register("SelectedGroup", typeof(DocumentTabGroup), typeof(TabbedDocumentView), new UIPropertyMetadata(null, OnSelectedGroupChanged));

		private static void OnSelectedGroupChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
		{
			((TabbedDocumentView)target).OnSelectedGroupChanged((DocumentTabGroup)e.OldValue, (DocumentTabGroup)e.NewValue);
		}
		protected virtual void OnSelectedGroupChanged(
			DocumentTabGroup oldGroup,
			DocumentTabGroup newGroup
			)
		{
			if (oldGroup != null)
			{
				oldGroup.SelectionChanged -= new SelectionChangedEventHandler(SelectedGroup_SelectionChanged);
			}

			if (this.SelectedGroup == null)
				this.SelectedItem = null;
			else
			{
				DocumentItem item = (DocumentItem)this.SelectedGroup.SelectedItem as DocumentItem;
				this.SelectedItem = item.DataContext;
			}

			if (newGroup != null)
			{
				newGroup.SelectionChanged += new SelectionChangedEventHandler(SelectedGroup_SelectionChanged);
			}
		}

		void SelectedGroup_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			DocumentItem container = (DocumentItem)this.SelectedGroup.SelectedItem;
			object item =
				(container != null)
				? container.DataContext
				: null
				;
			//object logicalParent = LogicalTreeHelper.GetParent(container);
			//object host = ItemsControl.ItemsControlFromItemContainer(container);
			//object item = this.ItemContainerGenerator.ItemFromContainer(container);
			this.SelectedItem = item;
		}

		void group_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
		{
			DocumentTabGroup group = (DocumentTabGroup)sender;
			if (group.IsKeyboardFocusWithin)
				this.SelectedGroup = group;
		}

		public bool IsGroupValid(int group)
		{
			return this._groupTable.ContainsKey(group);
		}
		protected DocumentTabGroup GetGroupContainer(int group)
		{
			return this._groupTable[group];
		}
		protected IList<DocumentItem> GetGroupItemsSource(int group)
		{
			DocumentTabGroup groupContainer = this.GetGroupContainer(group);
			if (groupContainer == null) return null;

			IList<DocumentItem> itemsSource = groupContainer.ItemsSource as IList<DocumentItem>;
			return itemsSource;
		}
		protected int GetGroup(DocumentTabGroup container)
		{
			if (container == null) throw new ArgumentNullException("container");

			return container._group;
		}
		#endregion

		#region Items
		//protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
		//{
		//    base.OnItemsChanged(e);

		//    if (e.Action == NotifyCollectionChangedAction.Add)
		//    {
		//        this.OnItemsAdded(e.NewItems, e.NewStartingIndex);
		//    }
		//    else if (e.Action == NotifyCollectionChangedAction.Remove)
		//    {
		//        this.OnRemoveItems(e.OldItems);
		//    }
		//}

		//private void OnItemsAdded(IList items, int startIndex)
		//{
		//}

		void Item_GroupChanged(object sender, EventArgs e)
		{
		}

		private void OnRemoveItems(IEnumerable items)
		{
		}

		protected override void OnSelectionChanged(SelectionChangedEventArgs e)
		{
			base.OnSelectionChanged(e);

			if (e.RemovedItems != null)
			{
				foreach (var item in e.RemovedItems)
				{
					DocumentItem container = (DocumentItem)this.ItemContainerGenerator.ContainerFromItem(item);
					if (container != null)
						container.IsSelected = false;
				}
			}
			if (e.AddedItems != null)
			{
				foreach (var item in e.AddedItems)
				{
					DocumentItem container = (DocumentItem)this.ItemContainerGenerator.ContainerFromItem(item);
					if (container != null)
					{
						container.IsSelected = true;
						container.IsSelectedWithinGroup = true;
					}
				}
			}
		}
		#endregion

		#region Floating

		public static readonly RoutedCommand FloatCommand;

		private void FloatCommand_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			object item = e.Parameter;
			if (item == null) return;

			DocumentItem container = item as DocumentItem;
			if (container == null) return;

			container.IsFloating = true;
		}

		void Item_IsFloatingChanged(object sender, EventArgs e)
		{
			DocumentItem container = (DocumentItem)sender;

			IList<DocumentItem> list = this.GetGroupItemsSource(container.Group);
			if (container.IsFloating)
			{
				if (list != null) list.Remove(container);
				this.Dispatcher.BeginInvoke((Action<DocumentItem>)this.ShowFloatingWindow, container);
			}
			else
			{
				if (list != null) list.Add(container);
				if (container.FloatingWindow != null)
					this.Dispatcher.BeginInvoke((Action)container.FloatingWindow.Close);
			}
		}

		private void ShowFloatingWindow(DocumentItem container)
		{
			FloatingWindow window = new FloatingWindow()
			{
				Owner = Application.Current.MainWindow,
				DataContext = container.DataContext,
				Style = this.FloatingWindowStyle,
				Template = this.FloatingWindowTemplate
			};

			window.SetBinding(Window.TitleProperty, new Binding() { Source = container, Path = new PropertyPath(DocumentItem.TitleProperty.Name) });

			ContentPresenter content = TabbedDocumentView.GetVisualTree(container);
			//this.AddLogicalChild(content);
			window.Content = content;
			container.FloatingWindow = window;
			window.Show();
		}

		public ControlTemplate FloatingWindowTemplate
		{
			get { return (ControlTemplate)GetValue(FloatingWindowTemplateProperty); }
			set { SetValue(FloatingWindowTemplateProperty, value); }
		}

		// Using a DependencyProperty as the backing store for FloatingContentTemplate.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty FloatingWindowTemplateProperty =
			DependencyProperty.Register("FloatingWindowTemplate", typeof(ControlTemplate), typeof(TabbedDocumentView), new UIPropertyMetadata(null));


		public Style FloatingWindowStyle
		{
			get { return (Style)GetValue(FloatingWindowStyleProperty); }
			set { SetValue(FloatingWindowStyleProperty, value); }
		}

		// Using a DependencyProperty as the backing store for FloatingWindowStyle.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty FloatingWindowStyleProperty =
			DependencyProperty.Register("FloatingWindowStyle", typeof(Style), typeof(TabbedDocumentView), new UIPropertyMetadata(null));
		#endregion
	}
}
